#include <alsa/asoundlib.h>  //ALSA头文件

int main(int argc, char** argv)
{
    snd_pcm_t* dev;

    //运行时需要指定音频文件
    if (argc < 2)
    {
        fprintf(stderr, "too few argument\n");
        return EXIT_FAILURE;
    }

    //打开放音设备，阻塞方式
    int error = snd_pcm_open(&dev, "hw:0,0", SND_PCM_STREAM_PLAYBACK, 0);
    if (error)
    {
        fprintf(stderr, "snd_pcm_open: %s\n", snd_strerror(error));
        return EXIT_FAILURE;
    }

    //设置放音参数
    snd_pcm_hw_params_t * record_param;
    //在栈上分配参数内存并初始化
    snd_pcm_hw_params_alloca(&record_param);
    error += snd_pcm_hw_params_any(dev, record_param);
    //设置采样格式（有符号16位整数，小端序）
    error += snd_pcm_hw_params_set_format(dev, record_param, SND_PCM_FORMAT_S16_LE);
    //设置访问方式（交错读写）
    error += snd_pcm_hw_params_set_access(dev, record_param, SND_PCM_ACCESS_RW_INTERLEAVED);
    //设置采样频率（16KHz）
    error += snd_pcm_hw_params_set_rate(dev, record_param, 16000, 0);
    //设置声道数量（单声道）
    error += snd_pcm_hw_params_set_channels(dev, record_param, 1);
    //根据硬件参数配置声卡
    error = snd_pcm_hw_params(dev, record_param);
    if (error)
    {
        fprintf(stderr, "snd_pcm_hw_params: %s\n", snd_strerror(error));
        return EXIT_FAILURE;
    }

    //获取一个周期数据量，从声卡获取数据时，读写的最小单位是1个周期
    snd_pcm_uframes_t nframe;
    int dir = 0;
    error = snd_pcm_hw_params_get_period_size(record_param, &nframe, &dir);
    if (error)
    {
        fprintf(stderr, "snd_pcm_hw_params_get_period_size: %s\n", snd_strerror(error));
        return EXIT_FAILURE;
    }

    printf("period = %lu frames\n", nframe);
    //分配缓冲区，大小必须为周期的倍数
    char* buf = malloc(snd_pcm_frames_to_bytes(dev, nframe));

    //打开音频文件
    FILE* fp = fopen(argv[1], "r+");
    if (!fp)
    {
        perror("fopen");
        free(buf);
        return EXIT_FAILURE;
    }

    //开始播放
    snd_pcm_prepare(dev);

    while (1)
    {
        size_t rbytes;
        //从文件中读取一个周期的音频数据
        rbytes = fread(buf, 1, snd_pcm_frames_to_bytes(dev, nframe), fp);
        if (rbytes == 0)
        {
            break;
        }

        //向最后一个周期的数据填充静音
        if (rbytes < snd_pcm_frames_to_bytes(dev, nframe))
        {
            memset(buf + rbytes, 0, snd_pcm_frames_to_bytes(dev, nframe) - rbytes);
        }

        //将音频数据写入声卡
        snd_pcm_sframes_t wframes = snd_pcm_writei(dev, buf, nframe);
        if (wframes < 0)
        {
            fprintf(stderr, "snd_pcm_writei: %s\n", snd_strerror(wframes));
        }
    }

    //停止并等待播放完成
    snd_pcm_drain(dev);

    fclose(fp);
    free(buf);

    //关闭设备
    snd_pcm_close(dev);

    return 0;
}